
class Object
    #
    # Returns true if the object describes a sensor, violation zone and so on
    #
    def sms?
        if self.kind_of?Sketchup::Entity
            if self.deleted?
                return false
            else
                return self.get_attribute("dynamic_attributes", 'typename', nil) != nil;
            end
        else
            return false
        end
    end
end

class Hash
    def sms_get(key, df)
        if self.has_key?(key)
            return self[key]
        else
            return df
        end
    end
    
    #
    # Merges deeply the hash tables that hold sms object's parameters 
    #
    def sms_merge!(htNew)
        parameters = self['Parameters']
        self.merge!(htNew)
        if not htNew['Parameters'].nil? and not parameters.nil?
            self['Parameters'] = parameters.merge(htNew['Parameters'])
        end
        #TODO how to deal with plugin devices, zones, their parameters?
        return self
    end
end

module Sketchup
    class Entity
        def sms_update
            $dc_observers.get_latest_class.redraw_with_undo(self) if sms?
        end
        
        def sms_id
            return self.get_attribute("dynamic_attributes", 'id', nil);
        end
        
        def sms_id=(id1)
            return self.set_attribute("dynamic_attributes", 'id', id1);
        end
        
        #
        # Returns entity's typename e.g. 'plugindevice', 'sensor'
        #
        def sms_typename
            return self.get_attribute("dynamic_attributes", 'typename', nil);
        end
        
        def sms_typename=(typename1)
            self.set_attribute("dynamic_attributes", 'typename', typename1);
        end
        
        #
        # Returns entity's typename e.g. 'plugindevice', 'sensor'
        #
        def sms_class
            return self.get_attribute("dynamic_attributes", 'typename', nil);
        end
        
        def sms_class=(class1)
            self.set_attribute("dynamic_attributes", 'typename', class1);
        end
        
        # in degrees
        def sms_rotphi
            return self.get_attribute("dynamic_attributes", 'rotphi', nil);
        end
        
        # in degrees
        def sms_rotphi=(value)
            return self.set_attribute("dynamic_attributes", 'rotphi', value);
        end
        
        # in degrees
        def sms_rottheta
            return self.get_attribute("dynamic_attributes", 'rottheta', nil);
        end
        
        # in degrees
        def sms_rottheta=(value)
            return self.set_attribute("dynamic_attributes", 'rottheta', value);
        end
        
        #
        # Returns a value of an attribute in the entity's dynamic attribute dictionary. 
        # Arguments: 
        #   key   An attribute key.
        #   default_value   (optional) A default value to return if no attribute is found.
        #
        def sms_get(*attr)
            return self.get_attribute("dynamic_attributes", attr[0], (attr.size == 2) ? attr[1] : nil);
        end

        #
        # Set the value of an attribute in a dynamic attribute dictionary.
        # 
        # Arguments:
        #   attr        An attribute key.
        #   value       The value for the attribute.
        #
        # Returns:
        #   value       The newly set value if successful. 
        #
        def sms_set(attr, value)
            return self.set_attribute("dynamic_attributes", attr, value);
        end
        
        def sms_sensor?()
            return self.sms_typename == 'sensor';
        end
        
        def sms_internal?
            return self.layer.sms_internal?;
        end
        
        #
        # Creates and returns a wrapper for the sms entity. 
        #
        def sms_wrapper
            return SmsRb::EntityWrapperFactory.new.create_from_entity(self);
        end
        
        def sms_w
            return self.sms_wrapper;
        end
    end
    
    class Material
        #
        # Returns a sms material that is mapped to this Sketchup's material. 
        #
        def sms_material
            self.get_attribute("sms", "material", "");
        end
        #
        # Maps this Sketchup's material to the given sms material. 
        #
        def sms_material=(value)
            self.set_attribute("sms", "material", value);
        end
    end
    
    class Layer
        def sms_internal?
            return self.name[0..0] == "~";
        end
    end
end

module Geom
    class Point3d
        #
        # Returns true if the points are close to each other
        #:return: => bool
        #:arg: point => Point3d
        #
        def almost_eql?(point)
            if point.nil? then return false; end
            return (self - point).length.to_f < 0.001
        end
    end
    
    class Vector3d
        # Compute the polygon's normal and a reference point on
        # the plane. Note that the actual reference point is
        # refpt / nverts
        def Vector3d.normal(points)
            norm = Geom::Vector3d.new(0, 0, 0);
            #refpt = Geom::Vector3d.new(0, 0, 0);
            (0 .. points.size - 1).each do |i|
                u = points[i];
                v = points[(i + 1)%points.size()];
                norm[0] += (u[1] - v[1]) * (u[2] + v[2]);
                norm[1] += (u[2] - v[2]) * (u[0] + v[0]);
                norm[2] += (u[0] - v[0]) * (u[1] + v[1]);
                #refpt = refpt + Geom::Vector3d.new(u.x, u.y, u.z);
            end
            norm.normalize!;
            return norm;
        end
    end
    
    class Transformation
        #
        # Converts the transform object so that it can be stored in sms entity's parameters directly. 
        # See EntityWrapper#set_data. 
        #
        def to_data
            htData = {}
            htData['PositionX'] = origin.x.to_m.to_f
            htData['PositionY'] = origin.y.to_m.to_f
            htData['PositionZ'] = origin.z.to_m.to_f
            
            quat = Geom::Quaternion.fromTransformation(self)
            htData['QuaternionX'] = quat[0]
            htData['QuaternionY'] = quat[1]
            htData['QuaternionZ'] = quat[2]
            htData['QuaternionW'] = quat[3]
            
            return htData
        end
        
        #
        # Converts the transform object from a form that is stored in sms entity's parameters. 
        # See EntityWrapper#get_data.  
        #
        def Transformation.from_data(htData)            
            x = htData['PositionX']
            y = htData['PositionY']
            z = htData['PositionZ']
            qx = htData['QuaternionX']
            qy = htData['QuaternionY']
            qz = htData['QuaternionZ']
            qw = htData['QuaternionW']
            if ( x and y and z and qx and qy  and qz  and qw )
                origin = Geom::Vector3d.new(x.to_f.m, y.to_f.m, z.to_f.m)
                transl = Geom::Transformation.translation(origin)
                
                quat = Geom::Quaternion.new(qx.to_f, qy.to_f, qz.to_f, qw.to_f)
                rot = quat.getTransformation()
                tr = transl * rot
            end
            return tr
        end
    end
end

